/*************************************************************************
 *                     COPYRIGHT NOTICE
 *            Copyright 2024 Horizon Robotics, Inc.
 *                   All rights reserved.
 *************************************************************************/
#ifndef GLES_WINSYS_HPP
#define GLES_WINSYS_HPP

#include <stdint.h>
#include <string>
#include <vector>
#include <gbm.h>
#include <GLES3/gl32.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <memory>
//#include "xdg-shell-client-protocol.h"
#include "drm_utils.hpp"
#include "gpu_sample_utils.hpp"


#include <X11/Xlib.h>
class egl_winsys
{
public:
	enum {
		INIT_SUCCESS,
		DUMP_SUCCESS = 0,
		RUN_SUCCESS = 0,
		INIT_FAILED,
		DUMP_FAILED,
		RUN_FAILED
	};
	EGLint pbuffer_attribs[5] =
	{
		EGL_WIDTH, 2,
		EGL_HEIGHT, 2,
		EGL_NONE
	};
	const EGLint context_attribs[5] =
	{
		EGL_CONTEXT_CLIENT_VERSION, 3,
		EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
		EGL_NONE
	};

	egl_winsys(OptionParser &optionParser);
	virtual ~egl_winsys() { /* Do nothing */ };
	virtual int32_t init(void) = 0;
	virtual void de_init(void) = 0;
	virtual int32_t dump_one_image(void) = 0;
	virtual int32_t flip() = 0;
	void make_draw_current(void);
	void swap(void);
	bool wait(void);
	int32_t set_gles_debug_output_context(void);

	OptionParser &win_options;
	int32_t win_w;
	int32_t win_h;
	EGLConfig egl_config;
	EGLDisplay egl_display;
	EGLNativeWindowType egl_window;
	EGLContext egl_context;
	EGLSurface egl_surface;

	Display * x11display = nullptr;
	EGLDisplay x11egldisplay = nullptr;
	EGLContext x11eglcontex = nullptr;
	EGLSurface x11eglsurface = nullptr;
	Window x11window;

	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;

protected:
	const struct WinsysConfig {
		// 不同窗口（rgb888/nv12)下的配置
		EGLint window_config_attribs[static_cast<uint8_t>(WinSysOutType::WIN_OUT_TYPE_NUM)][20] = {
			{
				EGL_SURFACE_TYPE,              EGL_WINDOW_BIT,
				EGL_RENDERABLE_TYPE,           EGL_OPENGL_ES3_BIT,
				EGL_COLOR_BUFFER_TYPE,         EGL_YUV_BUFFER_EXT,
				EGL_YUV_SUBSAMPLE_EXT,         EGL_YUV_SUBSAMPLE_4_2_0_EXT,
				EGL_YUV_NUMBER_OF_PLANES_EXT,  2,
				EGL_YUV_ORDER_EXT,             EGL_YUV_ORDER_YUV_EXT,
				EGL_YUV_DEPTH_RANGE_EXT,       EGL_YUV_DEPTH_RANGE_FULL_EXT,
				EGL_YUV_PLANE_BPP_EXT,         EGL_YUV_PLANE_BPP_8_EXT,
				EGL_YUV_CSC_STANDARD_EXT,      EGL_YUV_CSC_STANDARD_709_EXT,
				EGL_NONE
			},
			{
				EGL_SURFACE_TYPE,		EGL_WINDOW_BIT,
				EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES3_BIT,
				EGL_COLOR_BUFFER_TYPE,  EGL_RGB_BUFFER,
				EGL_RED_SIZE,			8,
				EGL_GREEN_SIZE,			8,
				EGL_BLUE_SIZE,			8,
				EGL_ALPHA_SIZE,			8,
				EGL_NONE
			}
		};
		int32_t drm_plane_index[static_cast<uint8_t>(WinSysOutType::WIN_OUT_TYPE_NUM)] = {0, 3};
	} winsys_config;

	inline const EGLint *getWinConfigsPtr(void)
	{
		return reinterpret_cast<const EGLint *>(winsys_config.window_config_attribs[static_cast<uint8_t>(win_out_type)]);
	}

	inline int32_t getDrmPlaneIndex(void)
	{
		return winsys_config.drm_plane_index[static_cast<uint8_t>(win_out_type)];
	}

	WinSysOutType win_out_type;
	uint8_t out_image_plane_num;
};

class egl_gbm_drm_init : public egl_winsys
{
public:
	egl_gbm_drm_init(OptionParser &optionParser);
	~egl_gbm_drm_init(void);
	int32_t init(void) override;
	void de_init(void) override;
	int32_t dump_one_image(void) override;
	int32_t flip() override;

	struct gbm_device *gbm_device;
	int32_t drm_fd;

private:
	struct DRMFBState
	{
		int32_t fd;
		gbm_bo* bo;
		uint32_t fb_id;
	};

	/* For simplicity, we fix gpu's export image format to nv12, which contains 2 planes */
	enum DUMP_OUT_IMAGE_INFO {
		DUMP_OUT_FILE_STR_MAX_LENGTH = 100,
	};

	int32_t init_drm();
	int32_t gbm_device_create(void);
	int32_t egl_create_window(void);
	DRMFBState* fb_get_from_bo(gbm_bo* bo);
	int32_t check_for_page_flip(int32_t timeout_ms);
	static void fb_destroy_callback(gbm_bo* bo, void* data);
	static void page_flip_handler(int32_t fd, uint32_t frame, uint32_t sec, uint32_t usec, void* data);

	bool enable_drm_;
	bool crtc_set_;
	uint32_t plane_id_;
	drmModeRes* resources_;
	drmModeConnector* connector_;
	drmModeEncoder* encoder_;
	drmModeCrtcPtr crtc_;
	drmModeModeInfo* mode_;
	drmModeAtomicReq *req_;
	gbm_bo* pending_bo_;
	gbm_bo* flipped_bo_;
	gbm_bo* presented_bo_;
	static void * user_data_;




};

#endif